﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Data;
using System.Windows.Input;
using EpiNEXT.Core;
using EpiNEXT.Core.Collections;
using EpiNEXT.Core.Fields;

namespace EpiNEXT.WPF
{
    public class CodeListViewModel : ObservableObject
    {
        #region Members
        private CodeList _codeList;
        private CodeListMode _codeListMode = CodeListMode.Basic;
        private object _listValuesLock = new object();
        #endregion Members

        #region Properties
        public CodeList CodeList 
        {
            get 
            {
                return this._codeList;
            }
            set 
            {
                this._codeList = value;
            }
        }

        public Guid Id
        {
            get
            {
                return CodeList.Id;
            }
            set
            {
                CodeList.Id = value;
                RaisePropertyChanged("Id");
            }
        }

        public CodeListMode Mode
        {
            get
            {
                return CodeList.Mode;
            }
            set
            {
                CodeList.Mode = value;
                RaisePropertyChanged("Mode");
            }
        }

        public bool Concatenate
        {
            get
            {
                return CodeList.Concatenate;
            }
            set
            {
                CodeList.Concatenate = value;
                RaisePropertyChanged("Concatenate");
            }
        }

        public string Separator
        {
            get
            {
                return CodeList.Separator;
            }
            set
            {
                CodeList.Separator = value;
                RaisePropertyChanged("Separator");
            }
        }

        public ObservableCollection<CodeListValue> CodeListValues
        {
            get
            {
                return CodeList.CodeListValues;
            }
            set
            {
                CodeList.CodeListValues = value;
                RaisePropertyChanged("CodeListValues");
            }
        }

        public ICollectionView CodeListValuesView
        {
            get;
            set;
        }

        public string Name
        {
            get
            {
                return CodeList.Name;
            }
            set
            {
                CodeList.Name = value;
                RaisePropertyChanged("Name");
            }
        }
        #endregion // Properties

        #region Constructors
        public CodeListViewModel(CodeList codeList)
        {
            this.CodeList = codeList;
            CodeListValuesView = new CollectionViewSource { Source = CodeList.CodeListValues }.View;
            System.Windows.Data.BindingOperations.EnableCollectionSynchronization(CodeListValuesView, _listValuesLock);
        }
        #endregion // Constructors

        #region Methods
        public override string ToString()
        {
            return this.Name;
        }
        #endregion // Methods
    }

    public class PageViewModel : ObservableObject
    {
        #region Members
        private EpiNEXT.Core.Page _page;
        #endregion // Members

        #region Properties
        public EpiNEXT.Core.Page Page
        {
            get { return this._page; }
            set
            {
                this._page = value;
            }
        }

        public Guid PageId
        {
            get { return Page.PageId; }
            set
            {
                Page.PageId = value;
                RaisePropertyChanged("PageId");
            }
        }

        public string Title
        {
            get { return Page.Title; }
            set
            {
                Page.Title = value;
                RaisePropertyChanged("Title");
            }
        }

        public ObservableCollection<IField> Fields
        {
            get { return Page.Fields; }
        }

        public Form Owner
        {
            get { return Page.Owner; }
            set
            {
                Page.Owner = value;
                RaisePropertyChanged("Owner");
            }
        }

        public FormViewModel FormVM { get; private set; }
        #endregion // Properties

        public PageViewModel(Page page, FormViewModel formVM)
        {
            this.Page = page;
            this.FormVM = formVM;
        }
    }

    public class FormViewModel : FormBaseViewModel
    {
        #region Members
        private static object _pagesLock = new object();
        private string _searchString = String.Empty;
        private ObservableCollection<PageViewModel> _pageCollection = new ObservableCollection<PageViewModel>();
        #endregion

        #region Properties

        public string SearchString
        {
            get { return this._searchString; }
            set
            {
                if (!SearchString.Equals(value))
                {
                    this._searchString = value;
                    RaisePropertyChanged("SearchString");
                }
            }
        }

        public bool IsRelatedForm
        {
            get { return Form.IsRelatedForm; }
            private set
            {
                Form.IsRelatedForm = value;
                RaisePropertyChanged("IsRelatedForm");
            }
        }

        public int MaxRecords
        {
            get { return Form.MaxRecords; }
            private set
            {
                Form.MaxRecords = value;
                RaisePropertyChanged("MaxRecords");
            }
        }

        public bool HasUnsavedChanges
        {
            get { return Form.HasUnsavedChanges; }
            private set
            {
                Form.HasUnsavedChanges = value;
                RaisePropertyChanged("HasUnsavedChanges");
            }
        }

        public Form ParentForm
        {
            get { return Form.ParentForm; }
            private set
            {
                Form.ParentForm = value;
                RaisePropertyChanged("ParentForm");
            }
        }

        public string Description
        {
            get { return (Form as Form).Description; }
            private set
            {
                (Form as Form).Description = value;
                RaisePropertyChanged("Description");
            }
        }

        public ICollectionView PagesView { get; set; }

        public int Version
        {
            get { return (Form as Form).Version; }
            private set
            {
                (Form as Form).Version = value;
                RaisePropertyChanged("Version");
            }
        }

        public double PageWidth
        {
            get { return (Form as Form).PageWidth; }
            private set
            {
                (Form as Form).PageWidth = value;
                RaisePropertyChanged("PageWidth");
            }
        }

        public double PageHeight
        {
            get { return (Form as Form).PageHeight; }
            private set
            {
                (Form as Form).PageHeight = value;
                RaisePropertyChanged("PageHeight");
            }
        }

        public double PageMarginTop
        {
            get { return (Form as Form).PageMarginTop; }
            private set
            {
                (Form as Form).PageMarginTop = value;
                RaisePropertyChanged("PageMarginTop");
            }
        }

        public double PageMarginLeft
        {
            get { return (Form as Form).PageMarginLeft; }
            private set
            {
                (Form as Form).PageMarginLeft = value;
                RaisePropertyChanged("PageMarginLeft");
            }
        }

        public Dictionary<ComboBoxField, CodeListViewModel> ListFieldLookup { get; set; }

        #endregion // Properties

        #region Constructor
        public FormViewModel(DataEntryInstrument form, ProjectViewModel projectVM)
            : base(form, projectVM)
        {
            this.ListFieldLookup = new Dictionary<ComboBoxField, CodeListViewModel>();
            this._pageCollection = new ObservableCollection<PageViewModel>();

            RaisePropertyChanged("RecordsView");

            if (Form is Form)
            {
                foreach (Page page in (Form as Form).Pages)
                {
                    PageViewModel pageVM = new PageViewModel(page, this);
                    _pageCollection.Add(pageVM);
                }

                RaisePropertyChanged("PagesView");
            }

            foreach (IField field in this.Fields)
            {
                // To prevent using the same VM for each field, and therefore sync'ing 
                // list values across fields (which is BAD!) create new VMs. TODO: Look into
                // a better solution...

                if (field is YesNoField)
                {
                    YesNoField yesNoField = field as YesNoField;

                    CodeList codeList = null;

                    if (yesNoField.IncludeUnknown == true)
                    {
                        codeList = this.Project.CodeListYNU;
                    }
                    else
                    {
                        codeList = this.Project.CodeListYN;
                    }
                    
                    CodeListViewModel vm = new CodeListViewModel(codeList);
                    ListFieldLookup.Add(yesNoField, vm);
                }
                else if (field is ComboBoxField) // must be an else-if since a YesNoField inherits from ComboBoxField
                {
                    ComboBoxField comboBoxField = field as ComboBoxField;

                    CodeList codeList = this.Project.GetCodeListById(comboBoxField.ListSource.Id);
                    CodeListViewModel vm = new CodeListViewModel(codeList);
                    ListFieldLookup.Add(comboBoxField, vm);
                }
            }
        }
        #endregion Constructor

        #region Methods
        /// <summary>
        /// Gets the level at which this form appears in a relational hierarchy.
        /// </summary>
        /// <returns>int; represents the level at which the form appears in the hierarchy. 0 = top-most parent, 1 = child of top-most parent, 2 = grandchild of top-most parent, etc.</returns>
        public int GetRelationalLevel()
        {
            return this.Form.GetRelationalLevel();
        }

        /// <summary>
        /// Enables collection sync on the key collection properties for the form view model, allowing them to be accessed on non-UI threads.
        /// </summary>
        public override void InitalizeCollectionSynchronization()
        {
            base.InitalizeCollectionSynchronization();

            PagesView = new CollectionViewSource { Source = _pageCollection }.View;
            System.Windows.Data.BindingOperations.EnableCollectionSynchronization(PagesView, _pagesLock);
        }

        /// <summary>
        /// Gets the appropriate code list view model for a given combo box field
        /// </summary>
        /// <param name="comboBoxField">The combo box field whose list values should be returned</param>
        /// <returns>The view model representing the list values for the combo box field</returns>
        public CodeListViewModel GetCodeListViewModelForField(ComboBoxField comboBoxField)
        {
            //if (comboBoxField is YesNoField)
            //{
            //    YesNoField ynField = comboBoxField as YesNoField;
            //    if (ynField.IncludeUnknown)
            //    {
            //        return this.ProjectVM.CodeListYesNoUnknownViewModel;
            //    }
            //    else
            //    {
            //        return this.ProjectVM.CodeListYesNoViewModel;
            //    }
            //}

            if (ListFieldLookup.ContainsKey(comboBoxField))
            {
                return ListFieldLookup[comboBoxField];
            }

            return null;
        }

        public object GetCurrentRecordValue(IField field)
        {
            Record currentRecord = this.RecordsView.CurrentItem as Record;
            if (currentRecord == null) return null;
            object value = currentRecord.GetFieldData(field);
            return value;
        }

        /// <summary>
        /// Deletes a record from the form's current set of records
        /// </summary>
        /// <param name="record">The record to remove</param>
        /// <returns>bool; whether the operation was successful</returns>
        public bool DeleteRecord(Record record)
        {
            return Form.DeleteRecord(record);
        }

        /// <summary>
        /// Adds a new record to the form's set of records
        /// </summary>
        /// <returns>record; the record that was added</returns>
        public Record AddRecord()
        {
            Record newRecord = null;

            if (this.IsRelatedForm)
            {
                newRecord = Form.AddRecord((ParentFormVM.RecordsView.CurrentItem as Record).Id);
            }
            else
            {
                newRecord = Form.AddRecord();
            }

            //if (this.ParentFormVM != null)
            //{
            //    Record parentRecord = this.ParentFormVM.RecordsView.CurrentItem as Record; // this shouldn't be null, otherwise how would we get here?
            //    newRecord.ForeignKey = parentRecord.Id; // attach FKEY
            //}

            //RaisePropertyChanged("RecordsView");

            return newRecord;
        }
        #endregion // Methods

        #region Commands
        public ICommand SearchRecords { get { return new RelayCommand(SearchRecordsExecute); } }
        private void SearchRecordsExecute()
        {
            try
            {
                string searchString = SearchString.Trim();//.ToLower();

                if (!String.IsNullOrEmpty(searchString))
                {
                    RecordsView.Filter = new Predicate<object>
                        (
                            record =>
                                ((Record)record).ContainsSearchValue(searchString)
                        );
                }
                else
                {
                    RecordsView.Filter = null;
                }
            }
            catch (Exception ex)
            {
                //System.Security.Principal.WindowsIdentity.GetCurrent().Name.ToString() + ": " +
                //"Searching for " + SearchString + " in cases list generated exception with message: " + ex.Message);
                RecordsView.Filter = null;
                SearchString = String.Empty;
            }
        }
        #endregion // Commands
    }

    public class DataGridTableViewModel : FormBaseViewModel
    {
        public DataGridTableViewModel(DataEntryInstrument form, ProjectViewModel projectVM)
            : base(form, projectVM)
        {
            RaisePropertyChanged("RecordsView");
        }
    }

    public class ProjectViewModel : ObservableObject
    {
        #region Members
        private bool _isShowingPrintPreview = false;
        private bool _isShowingSearch = false;
        private bool _isShowingSave = false;
        private bool _isShowingErrorPanel = false;
        private bool _showTabIndices = false;
        private string _currentStatusMessage = String.Empty;
        private string _currentField = String.Empty;
        private Project _project;
        private bool _isLoadingProject = false;
        public bool _isLoaded = false;
        private static object _recordsLock = new object();
        private static object _formsLock = new object();
        ObservableCollection<FormViewModel> _formCollection = new ObservableCollection<FormViewModel>();
        ObservableCollection<DataGridTableViewModel> _dataGridTableCollection = new ObservableCollection<DataGridTableViewModel>();
        ObservableCollection<CodeListViewModel> _codeListCollection = new ObservableCollection<CodeListViewModel>();
        #endregion // Members

        #region Properties
        //public ICollectionView DataEntryRecordsCollectionView { get; set; }
        public ICollectionView FormsCollectionView { get; set; }
        public ICollectionView DataGridTablesCollectionView { get; set; }
        public ICollectionView CodeListCollectionView { get; set; }

        public bool IsShowingPrintPreview
        {
            get { return this._isShowingPrintPreview; }
            set
            {
                if (IsShowingPrintPreview != value)
                {
                    if (!IsLoaded || IsLoadingProject || !CanExecuteRecordSaveCommands())
                    {
                        this._isShowingPrintPreview = false;
                    }
                    else
                    {
                        this._isShowingPrintPreview = value;
                    }
                    RaisePropertyChanged("IsShowingPrintPreview");
                }
            }
        }

        public bool IsShowingSearch
        {
            get { return this._isShowingSearch; }
            set
            {
                if (IsShowingSearch != value)
                {
                    this._isShowingSearch = value;
                    RaisePropertyChanged("IsShowingSearch");
                }
            }
        }

        public bool IsShowingSave
        {
            get { return this._isShowingSave; }
            set
            {
                if (IsShowingSave != value)
                {
                    this._isShowingSave = value;
                    RaisePropertyChanged("IsShowingSave");
                }
            }
        }

        public bool IsShowingErrorPanel
        {
            get { return this._isShowingErrorPanel; }
            set
            {
                if (IsShowingErrorPanel != value)
                {
                    this._isShowingErrorPanel = value;
                    RaisePropertyChanged("IsShowingErrorPanel");
                }
            }
        }

        public bool IsShowingTabIndices
        {
            get { return this._showTabIndices; }
            set
            {
                this._showTabIndices = value;
                RaisePropertyChanged("IsShowingTabIndices");
            }
        }

        public string CurrentField
        {
            get { return this._currentField; }
            set
            {
                if (this._currentField != value)
                {
                    this._currentField = value;
                    RaisePropertyChanged("CurrentField");
                }
            }
        }

        public string CurrentStatusMessage
        {
            get { return _currentStatusMessage; }
            set
            {
                _currentStatusMessage = value;
                RaisePropertyChanged("CurrentStatusMessage");
            }
        }

        public Guid ProjectId
        {
            get { return Project.ProjectId; }
            set
            {
                Project.ProjectId = value;
                RaisePropertyChanged("ProjectId");
            }
        }

        public string Author
        {
            get { return Project.Author; }
            set
            {
                Project.Author = value;
                RaisePropertyChanged("Author");
            }
        }

        public DateTime DateCreated
        {
            get { return Project.DateCreated; }
            set
            {
                Project.DateCreated = value;
                RaisePropertyChanged("DateCreated");
            }
        }

        public DateTime DateLastSaved
        {
            get { return Project.DateLastSaved; }
            set
            {
                Project.DateLastSaved = value;
                RaisePropertyChanged("DateLastSaved");
            }
        }

        public long LastSaved
        {
            get { return Project.LastSaved; }
            set
            {
                Project.LastSaved = value;
                RaisePropertyChanged("LastSaved");
            }
        }

        public long Created
        {
            get { return Project.Created; }
            set
            {
                Project.Created = value;
                RaisePropertyChanged("Created");
            }
        }

        public string Title
        {
            get { return Project.Title; }
            set
            {
                Project.Title = value;
                RaisePropertyChanged("Title");
            }
        }

        public string Summary
        {
            get { return Project.Summary; }
            set
            {
                Project.Summary = value;
                RaisePropertyChanged("Summary");
            }
        }

        public string Description
        {
            get { return Project.Description; }
            set
            {
                Project.Description = value;
                RaisePropertyChanged("Description");
            }
        }

        public FormViewModel SelectedForm
        {
            get
            {
                if (FormsCollectionView == null) return null;
                return FormsCollectionView.CurrentItem as FormViewModel;
            }
        }

        public Guid ParentRecordId { get; private set; }

        public bool IsLoadingProject
        {
            get { return this._isLoadingProject; }
            set
            {
                this._isLoadingProject = value;
                RaisePropertyChanged("IsLoadingProject");
            }
        }

        public bool IsLoaded
        {
            get { return this._isLoaded; }
            set
            {
                if (IsLoaded != value)
                {
                    this._isLoaded = value;
                    RaisePropertyChanged("IsLoaded");
                }
            }
        }

        public bool IsProcessing
        {
            get 
            {
                if (IsLoadingProject)
                {
                    return true;
                }
                else
                {
                    if (Project == null || Project.Database == null)
                    {
                        return false;
                    }
                    else
                    {
                        return Project.Database.IsProcessing;
                    }
                }
            }
        }

        public Project Project
        {
            get { return this._project; }
            private set
            {
                this._project = value;
                RaisePropertyChanged("Project");
            }
        }

        public CodeListViewModel CodeListYesNoViewModel { get; set; }

        public CodeListViewModel CodeListYesNoUnknownViewModel { get; set; }

        public bool HasUnsavedChanges
        {
            get
            {
                // check current item first, this is most efficient
                if (SelectedForm == null) return false;
                if (SelectedForm.RecordsView.CurrentItem != null)
                {
                    if ((SelectedForm.RecordsView.CurrentItem as Record).HasUnsavedChanges == true)
                    {
                        return true;
                    }
                    if ((SelectedForm.RecordsView.CurrentItem as Record).IsInDatabase == false)
                    {
                        return true;
                    }
                }

                foreach (FormViewModel formVM in this.FormsCollectionView)
                {
                    foreach (Record record in formVM.RecordsView)
                    {
                        if (record.HasUnsavedChanges == true || record.IsInDatabase == false)
                        {
                            return true;
                        }
                    }
                }

                foreach (DataGridTableViewModel dataGridTable in this.DataGridTablesCollectionView)
                {
                    foreach (Record record in dataGridTable.RecordsView)
                    {
                        if (record.HasUnsavedChanges == true || record.IsInDatabase == false)
                        {
                            return true;
                        }
                    }
                }

                return false;
            }
        }
        #endregion // Properties

        #region Constructors
        public ProjectViewModel()
        {
            IsLoadingProject = false;
        }
        #endregion // Constructors

        #region Methods
        public void SetSelectedForm(Guid id)
        {
            FormViewModel proposedFormVM = this.GetFormById(id);

            Task.Factory.StartNew(() =>
            {
                // get level of the (soon-to-be) previous form
                int previousLevel = SelectedForm.GetRelationalLevel();
                Guid proposedParentRecordId = new Guid("00000000000000000000000000000000");
                if (SelectedForm.RecordsView != null && SelectedForm.RecordsView.CurrentItem != null)
                {
                    proposedParentRecordId = ((Record)SelectedForm.RecordsView.CurrentItem).Id;
                }
                // change the current form
                this.FormsCollectionView.MoveCurrentTo(proposedFormVM);

                // find out the level of the now-currently-selected form
                int currentLevel = SelectedForm.GetRelationalLevel();

                // if current level > previous level then we navigated to a child form
                if (currentLevel > previousLevel && SelectedForm.ParentFormVM != null && SelectedForm.ParentFormVM.RecordsView != null)
                {
                    ParentRecordId = proposedParentRecordId;
                    string parentRecordIdString = ParentRecordId.ToString();
                    using (SelectedForm.RecordsView.DeferRefresh())
                    {
                        SelectedForm.RecordsView.Filter = obj =>
                        {
                            Record record = obj as Record;
                            if (record.ForeignKeyString.Equals(parentRecordIdString)) return true;
                            else return false;
                        };
                    }
                }
                else if (currentLevel < previousLevel && SelectedForm.ParentFormVM != null && SelectedForm.ParentFormVM.RecordsView != null)
                {
                    // we're navigating back to a parent
                }

                RaisePropertyChanged("SelectedForm");
            }, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default);
        }

        private void LoadProject(ProjectInfo projectInfo, FormInfo formInfo = null)
        {
            ProjectFactory factory = new ProjectFactory();
            Project = factory.CreateProject(projectInfo.FileInfo.FullName);

            _codeListCollection = new ObservableCollection<CodeListViewModel>();
            foreach (CodeList codeList in Project.CodeLists)
            {
                CodeListViewModel codeListVM = new CodeListViewModel(codeList);
                _codeListCollection.Add(codeListVM);
            }
            CodeListCollectionView = new CollectionViewSource { Source = _codeListCollection }.View;
            RaisePropertyChanged("CodeListCollectionView");

            CodeListYesNoViewModel = new CodeListViewModel(Project.CodeListYN);
            CodeListYesNoUnknownViewModel = new CodeListViewModel(Project.CodeListYNU);

            _formCollection = new ObservableCollection<FormViewModel>();
            foreach (Form form in Project.Forms)
            {
                FormViewModel formVM = new FormViewModel(form, this);
                _formCollection.Add(formVM);
            }
            FormsCollectionView = new CollectionViewSource { Source = _formCollection }.View;
            RaisePropertyChanged("FormsCollectionView");

            foreach (FormViewModel formVM in FormsCollectionView)
            {
                formVM.SetParentForm();
            }

            _dataGridTableCollection = new ObservableCollection<DataGridTableViewModel>();
            foreach (DataGridTable dgTable in Project.DataGridTables)
            {
                DataGridTableViewModel dgtVM = new DataGridTableViewModel(dgTable, this);
                _dataGridTableCollection.Add(dgtVM);
            }
            DataGridTablesCollectionView = new CollectionViewSource { Source = _dataGridTableCollection }.View;
            RaisePropertyChanged("DataGridTablesCollectionView");

            foreach (DataGridTableViewModel dgtVM in DataGridTablesCollectionView)
            {
                dgtVM.SetParentForm();
            }

            if (formInfo != null)
            {
                foreach (FormViewModel formVM in this.FormsCollectionView)
                {
                    if (formVM.FormId.Equals(formInfo.Id))
                    {
                        SetSelectedForm(formVM.FormId);
                        break;
                    }
                }
            }
        }

        public virtual void InitalizeCollectionSynchronization()
        {
            System.Windows.Data.BindingOperations.EnableCollectionSynchronization(FormsCollectionView, _formsLock);
        }

        private bool CanExecuteMoveToNextRecordCommand()
        {
            if (SelectedForm == null) return false;
            if (SelectedForm.RecordsView == null) return false;
            if (SelectedForm.RecordsView.CurrentPosition == (SelectedForm.RecordsView.Cast<Record>().Count() - 1)) return false;

            Record currentRecord = SelectedForm.RecordsView.CurrentItem as Record;
            if (currentRecord == null)
            {
                return false;
            }
            else
            {
                return !(currentRecord.HasErrors);
            }
        }

        private bool CanExecuteMoveToPreviousRecordCommand()
        {
            if (SelectedForm == null) return false;
            if (SelectedForm.RecordsView == null) return false;
            if (SelectedForm.RecordsView.CurrentPosition == 0) return false;

            Record currentRecord = SelectedForm.RecordsView.CurrentItem as Record;
            if (currentRecord == null)
            {
                return false;
            }
            else
            {
                return !(currentRecord.HasErrors);
            }
        }

        private bool CanExecuteMoveToFirstRecordCommand()
        {
            return CanExecuteMoveToPreviousRecordCommand();
        }

        private bool CanExecuteMoveToLastRecordCommand()
        {
            return CanExecuteMoveToNextRecordCommand();
        }

        private bool CanExecuteTryUnloadFormCommands()
        {
            if (IsLoadingProject || IsProcessing)
            {
                return false;
            }
            if (SelectedForm == null || SelectedForm.RecordsView == null)
            {
                return false;
            }
            else
            {
                Record currentRecord = SelectedForm.RecordsView.CurrentItem as Record;
                if (currentRecord == null)
                {
                    return true;
                }
                else
                {
                    return !(currentRecord.HasErrors);
                }
            }
        }

        private bool CanExecuteRecordSaveCommands()
        {
            if (IsLoadingProject || IsProcessing)
            {
                return false;
            }
            if (SelectedForm == null || SelectedForm.RecordsView == null)
            {
                return false;
            }
            else
            {
                Record currentRecord = SelectedForm.RecordsView.CurrentItem as Record;
                if (currentRecord == null)
                {
                    return false;
                }
                else
                {
                    return !(currentRecord.HasErrors);
                }
            }
        }

        private bool CanExecuteRecordAddCommands()
        {
            if (IsLoadingProject || IsProcessing)
            {
                return false;
            }
            if (SelectedForm == null || SelectedForm.RecordsView == null)
            {
                return false;
            }
            else
            {
                Record currentRecord = SelectedForm.RecordsView.CurrentItem as Record;
                if (currentRecord == null)
                {
                    return true;
                }
                else
                {
                    return !(currentRecord.HasErrors);
                }
            }
        }

        public FormViewModel GetFormById(Guid id)
        {
            foreach (FormViewModel form in this.FormsCollectionView)
            {
                if (form.FormId.Equals(id))
                {
                    return form;
                }
            }
            throw new InvalidOperationException("Requested form does not exist.");
        }

        public CodeListViewModel GetCodeListById(Guid id)
        {
            foreach (CodeListViewModel codeList in this.CodeListCollectionView)
            {
                if (codeList.Id.Equals(id))
                {
                    return codeList;
                }
            }
            throw new InvalidOperationException("Requested form does not exist.");
        }

        public DataGridTableViewModel GetDataGridTableById(Guid id)
        {
            foreach (DataGridTableViewModel dgTable in this.DataGridTablesCollectionView)
            {
                if (dgTable.FormId.Equals(id))
                {
                    return dgTable;
                }
            }
            throw new InvalidOperationException("Requested data grid table does not exist.");
        }

        public ICommand UpgradeTemplateCommand { get { return new RelayCommand<string>(UpgradeTemplate); } }
        private void UpgradeTemplate(string filePath)
        {
            EpiNEXT.Core.Legacy.EI7ProjectTemplateConverter converter = new Core.Legacy.EI7ProjectTemplateConverter(filePath);
            Project newProject = converter.Convert();

            //string fileName = System.IO.Path.Combine(@"C:\Users\bioha_000\Documents\Visual Studio 2013\Projects\EpiNEXT\EpiNEXT.WPF.View\bin\Debug\Projects", newProject.Title + ".prj");
            //string fileName = System.IO.Path.Combine(@"C:\Users\knu1\Source\Workspaces\epinext\EpiNEXT\EpiNEXT.WPF.View\bin\Debug\Projects", newProject.Title + ".prj");
            string fileName = System.IO.Path.Combine(@"C:\Users\EIAdmin\Source\Workspaces\epinext\EpiNEXT\EpiNEXT.WPF.View\bin\Debug\Projects", newProject.Title + ".prj");
            newProject.ProjectFileInfo = new System.IO.FileInfo(fileName);

            newProject.Save();
            newProject.Dispose();
        }

        public ICommand ToggleSearchCommand { get { return new RelayCommand(ToggleSearch, CanExecuteRecordSaveCommands); } }
        private void ToggleSearch()
        {
            IsShowingSearch = !IsShowingSearch;
            SelectedForm.SearchString = String.Empty;
            SelectedForm.SearchRecords.Execute(null);
            if (SelectedForm.RecordsView.CurrentItem == null)
            {
                SelectedForm.RecordsView.MoveCurrentToFirst();
            }
        }

        public ICommand HideSearchCommand { get { return new RelayCommand(HideSearch); } }
        private void HideSearch()
        {
            IsShowingSearch = false;
            SelectedForm.SearchString = String.Empty;
            SelectedForm.SearchRecords.Execute(null);
            if (SelectedForm.RecordsView.CurrentItem == null)
            {
                SelectedForm.RecordsView.MoveCurrentToFirst();
            }
        }

        public ICommand ToggleErrorDisplayCommand { get { return new RelayCommand(ToggleErrorDisplay); } }
        private void ToggleErrorDisplay()
        {
            IsShowingErrorPanel = !IsShowingErrorPanel;
        }

        public ICommand ToggleTabIndicesCommand { get { return new RelayCommand(ToggleTabIndices); } }
        private void ToggleTabIndices()
        {
            IsShowingTabIndices = !IsShowingTabIndices;
        }

        public ICommand TogglePrintPreviewCommand { get { return new RelayCommand(TogglePrintPreview, CanExecuteRecordSaveCommands); } }
        private void TogglePrintPreview()
        {
            IsShowingPrintPreview = !IsShowingPrintPreview;
        }

        public ICommand TryUnloadFormCommand { get { return new RelayCommand(TryUnloadForm, CanExecuteTryUnloadFormCommands); } }
        private void TryUnloadForm()
        {
            if (HasUnsavedChanges)
            {
                IsShowingSave = !IsShowingSave;
            }
            else if(UnloadFormCommand.CanExecute(null))
            {
                UnloadFormCommand.Execute(null);
            }
        }

        public ICommand ExecuteProgramCommand { get { return new RelayCommand<string>(ExecuteProgram); } }
        private void ExecuteProgram(string path)
        {
            System.Diagnostics.Process proc = new System.Diagnostics.Process();
            proc.StartInfo.FileName = path;
            proc.StartInfo.UseShellExecute = true;
            proc.Start();
        }

        public ICommand GoToFormCommand { get { return new RelayCommand<Guid>(GoToForm); } }
        private void GoToForm(Guid formIdToOpen)
        {
            SetSelectedForm(formIdToOpen);
        }

        public ICommand UpdateComboBoxTargetsCommand { get { return new RelayCommand<ComboBoxField>(UpdateComboBoxTargets); } }
        private void UpdateComboBoxTargets(ComboBoxField comboBoxField)
        {
            UpdateOtherFields();

            // TODO: Can this be moved to the Project class and out of the VM?
            foreach (ListDestinationField destinationField in comboBoxField.ListDestinationFields)
            {
                if (SelectedForm.Fields.Contains(destinationField.Id))
                {
                    IField field = SelectedForm.Fields[destinationField.Id];
                    string columnName = destinationField.TargetName;

                    Record currentRecord = (Record)(SelectedForm.RecordsView.CurrentItem);

                    if (currentRecord != null)
                    {
                        string destinationValue = String.Empty;

                        object rawSourceValue = currentRecord.GetFieldData(comboBoxField);

                        if (rawSourceValue != null)
                        {
                            string sourceValue = rawSourceValue.ToString();

                            foreach (CodeListValue codeListValue in comboBoxField.ListSource.CodeListValues)
                            {
                                if (codeListValue.Store.Equals(sourceValue))
                                {
                                    foreach (CodeListTargetValue targetValue in codeListValue.TargetValues)
                                    {
                                        if (targetValue.Name.Equals(columnName))
                                        {
                                            destinationValue = targetValue.Value;
                                            break;
                                        }
                                    }

                                    break;
                                }
                            }
                        }

                        currentRecord.AddOrUpdateFieldData(field, destinationValue);
                    }
                }
            }
        }

        public ICommand UpdateComboBoxFilterCommand { get { return new RelayCommand<ComboBoxField>(UpdateComboBoxFilter); } }
        private void UpdateComboBoxFilter(ComboBoxField field)
        {
            string currentUICulture = System.Threading.Thread.CurrentThread.CurrentUICulture.ToString();

            if (SelectedForm.RecordsView.CurrentItem == null) return;

            UpdateOtherFields();

            foreach (KeyValuePair<ComboBoxField, CodeListViewModel> kvp in SelectedForm.ListFieldLookup)
            {
                ComboBoxField destinationField = kvp.Key;
                CodeListViewModel codeListVM = kvp.Value;

                if (destinationField.ListSourceCascadeSourceFieldId.Equals(field.FieldId))
                {
                    ComboBoxField sourceField = SelectedForm.Fields[destinationField.ListSourceCascadeSourceFieldId] as ComboBoxField;
                    string currentValue = String.Empty;
                    if ((SelectedForm.RecordsView.CurrentItem as Record).GetFieldData(destinationField as IField) != null)
                    {
                        currentValue = (SelectedForm.RecordsView.CurrentItem as Record).GetFieldData(destinationField as IField).ToString();
                    }
                    string sourceValue = String.Empty;
                    if ((SelectedForm.RecordsView.CurrentItem as Record).GetFieldData(sourceField as IField) != null)
                    {
                        sourceValue = (SelectedForm.RecordsView.CurrentItem as Record).GetFieldData(sourceField as IField).ToString();
                    }

                    ICollectionView view = codeListVM.CodeListValuesView;

                    using (view.DeferRefresh())
                    {
                        view.Filter = new Predicate<object>
                            (
                                    listValue =>
                                        (
                                        ((CodeListValue)listValue).Culture == null || 
                                        String.IsNullOrEmpty(((CodeListValue)listValue).Culture) || 
                                        ((CodeListValue)listValue).Culture == currentUICulture
                                        )
                                        &&
                                        ((CodeListValue)listValue).SourceValue == sourceValue
                            );
                    }

                    //CodeListValue selectedListValue = null;
                    foreach (CodeListValue codeListValue in view)
                    {
                        if (codeListValue.Store.Equals(currentValue))
                        {
                            view.MoveCurrentTo(codeListValue);
                        }
                    }
                }
                else if (destinationField.ListSourceCascadeSourceFieldId.Equals(new Guid("00000000-0000-0000-0000-000000000000")))
                {
                    ICollectionView view = codeListVM.CodeListValuesView;
                    if (view.Filter == null)
                    {
                        using (view.DeferRefresh())
                        {
                            view.Filter = new Predicate<object>
                                (
                                        listValue =>
                                            ((CodeListValue)listValue).Culture == null ||
                                            String.IsNullOrEmpty(((CodeListValue)listValue).Culture) || 
                                            ((CodeListValue)listValue).Culture == currentUICulture
                                );
                        }
                    }
                }
            }
        }

        private void UpdateOtherFields()
        {
            if (SelectedForm.RecordsView.CurrentItem == null) return;

            foreach (TextBoxField textField in this.SelectedForm.Fields.TextFields)
            {
                if (textField.IsOtherBox)
                {
                    IField sourceField = SelectedForm.Fields[textField.OtherSourceId];

                    if (sourceField is ComboBoxField)
                    {
                        ComboBoxField comboBoxField = sourceField as ComboBoxField;

                        CodeListViewModel codeListVM = SelectedForm.GetCodeListViewModelForField(comboBoxField);

                        foreach (CodeListValue codeListValue in codeListVM.CodeListValuesView)
                        {
                            if (codeListValue.IsOtherValue)
                            {
                                string listValue = codeListValue.Store;
                                object recordValue = SelectedForm.GetCurrentRecordValue(comboBoxField);

                                if (recordValue != null && listValue.Equals(recordValue.ToString()))
                                {
                                    textField.IsCurrentlyEnabled = true;
                                }
                                else
                                {
                                    textField.IsCurrentlyEnabled = false;
                                    (SelectedForm.RecordsView.CurrentItem as Record).AddOrUpdateFieldData(textField, String.Empty);
                                }
                            }
                        }
                    }
                }
            }
        }

        private void UpdateFilters()
        {
            foreach (DataGridTableViewModel tableVM in this.DataGridTablesCollectionView)
            {
                tableVM.RecordsView.Refresh();
            }

            foreach (IField field in this.SelectedForm.Fields)
            {
                if (field is ComboBoxField)
                {
                    UpdateComboBoxFilter(field as ComboBoxField);
                }
            }

            foreach (TextBoxField textField in this.SelectedForm.Fields.TextFields)
            {
                if (textField.IsOtherBox)
                {
                    IField sourceField = SelectedForm.Fields[textField.OtherSourceId];

                    if (sourceField is ComboBoxField)
                    {
                        ComboBoxField comboBoxField = sourceField as ComboBoxField;

                        CodeListViewModel codeListVM = SelectedForm.GetCodeListViewModelForField(comboBoxField);

                        if (codeListVM.CodeListValuesView.CurrentItem == null)
                        {
                            textField.IsCurrentlyEnabled = false;
                        }
                        else if ((codeListVM.CodeListValuesView.CurrentItem as CodeListValue).IsOtherValue == true)
                        {
                            textField.IsCurrentlyEnabled = true;
                        }
                        else
                        {
                            textField.IsCurrentlyEnabled = false;
                        }
                    }
                }
            }
        }

        private void Reset()
        {
            IsShowingSave = false;
            IsShowingSearch = false;
            IsShowingPrintPreview = false;
            IsShowingErrorPanel = false;
            IsShowingTabIndices = false;
            IsLoaded = false;
        }
        #endregion // Methods

        #region Commands

        public ICommand UnloadFormCommand { get { return new RelayCommand(UnloadFormCommandExecute); } }
        private void UnloadFormCommandExecute()
        {
            IsLoaded = false;
            Project.Dispose();
            Project = null;
            GC.Collect(0, GCCollectionMode.Forced, true);
            Reset();
        }

        public ICommand LoadFormCommand { get { return new RelayCommand<FormInfo>(LoadFormCommandExecute); } }
        private void LoadFormCommandExecute(FormInfo formInfo)
        {
            var context = TaskScheduler.FromCurrentSynchronizationContext();

            IsLoadingProject = true;
            Task.Factory.StartNew(() =>
            {
                LoadProject(formInfo.ProjectInfo, formInfo);
            }, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default
            ).ContinueWith(t =>
            {
                InitalizeCollectionSynchronization();

                foreach (FormViewModel formVM in this.FormsCollectionView)
                {
                    formVM.InitalizeCollectionSynchronization();
                }

                foreach (DataGridTableViewModel tableVM in this.DataGridTablesCollectionView)
                {
                    tableVM.InitalizeCollectionSynchronization();
                    tableVM.RecordsView.Filter = obj =>
                    {
                        Record record = obj as Record;

                        if (SelectedForm.RecordsView.CurrentItem == null) return false;

                        if (record.ForeignKey.Equals((SelectedForm.RecordsView.CurrentItem as Record).Id)) return true;
                        else return false;
                    };
                    tableVM.RecordsView.Refresh();
                }

                UpdateFilters();

                IsLoadingProject = false;
                IsLoaded = true;
            }, context);
        }

        public ICommand MoveToNextRecordCommand { get { return new RelayCommand(MoveToNextRecordExecute, CanExecuteMoveToNextRecordCommand); } }
        private void MoveToNextRecordExecute()
        {
            SelectedForm.RecordsView.MoveCurrentToNext();
            UpdateFilters();
        }

        public ICommand MoveToPreviousRecordCommand { get { return new RelayCommand(MoveToPreviousRecordExecute, CanExecuteMoveToPreviousRecordCommand); } }
        private void MoveToPreviousRecordExecute()
        {
            SelectedForm.RecordsView.MoveCurrentToPrevious();
            UpdateFilters();
        }

        public ICommand MoveToFirstRecordCommand { get { return new RelayCommand(MoveToFirstRecordExecute, CanExecuteMoveToFirstRecordCommand); } }
        private void MoveToFirstRecordExecute()
        {
            SelectedForm.RecordsView.MoveCurrentToFirst();
            UpdateFilters();
        }

        public ICommand MoveToLastRecordCommand { get { return new RelayCommand(MoveToLastRecordExecute, CanExecuteMoveToLastRecordCommand); } }
        private void MoveToLastRecordExecute()
        {
            SelectedForm.RecordsView.MoveCurrentToLast();
            UpdateFilters();
        }

        public ICommand SaveRecordAsyncCommand { get { return new RelayCommand(SaveRecordAsyncExecute, CanExecuteRecordSaveCommands); } }
        private void SaveRecordAsyncExecute()
        {
            CurrentStatusMessage = "Saving...";
            Task<bool> success = Project.Database.SaveRecordAsync(SelectedForm.Form as Form, SelectedForm.RecordsView.CurrentItem as Record);
        }

        public ICommand SaveRecordAndCloseAsyncCommand { get { return new RelayCommand(SaveRecordAndCloseAsyncExecute, CanExecuteRecordSaveCommands); } }
        private void SaveRecordAndCloseAsyncExecute()
        {
            CurrentStatusMessage = "Saving...";

            if (UnloadFormCommand.CanExecute(null))
            {
                try
                {
                    Project.Database.SaveRecord(SelectedForm.Form as Form, SelectedForm.RecordsView.CurrentItem as Record);
                    UnloadFormCommand.Execute(null);
                }
                catch (System.IO.IOException ex)
                {
                    
                }
            }
        }

        public ICommand SaveRecordCommand { get { return new RelayCommand(SaveRecordExecute, CanExecuteRecordSaveCommands); } }
        private void SaveRecordExecute()
        {
            CurrentStatusMessage = "Saving...";
            bool success = Project.Database.SaveRecord(SelectedForm.Form as Form, SelectedForm.RecordsView.CurrentItem as Record);
        }

        public ICommand DeleteRecordCommand { get { return new RelayCommand(DeleteRecordExecute, CanExecuteRecordSaveCommands); } }
        private void DeleteRecordExecute()
        {
            CurrentStatusMessage = "Deleting record...";
            Record currentRecord = SelectedForm.RecordsView.CurrentItem as Record;
            SelectedForm.DeleteRecord(currentRecord);
            UpdateFilters();
        }

        public ICommand AddRecordCommand { get { return new RelayCommand(AddRecordExecute, CanExecuteRecordAddCommands); } }
        private void AddRecordExecute()
        {
            CurrentStatusMessage = "Adding record...";
            Record record = SelectedForm.AddRecord();
            SelectedForm.RecordsView.MoveCurrentTo(record);

            UpdateFilters();
        }

        public ICommand ReturnToParentCommand { get { return new RelayCommand(ReturnToParentExecute, CanExecuteRecordAddCommands); } }
        private void ReturnToParentExecute()
        {
            SetSelectedForm(this.SelectedForm.ParentFormVM.FormId);
        }

        public ICommand ReturnToTopMostParentCommand { get { return new RelayCommand(ReturnToTopMostParentExecute, CanExecuteRecordAddCommands); } }
        private void ReturnToTopMostParentExecute()
        {
            FormViewModel formVM = this.SelectedForm.ParentFormVM;

            bool found = false;
            while (!found)
            {
                if (formVM.ParentFormVM != null)
                {
                    formVM = formVM.ParentFormVM;
                }
                else
                {
                    found = true;
                }
            }

            SetSelectedForm(formVM.FormId);
        }

        #endregion // Commands
    }
}
